package chat;

import java.io.Serializable;
import java.util.Hashtable;
import java.util.Map;

/**
 * Class defining a vector clock with a Map.
 * 
 * @author Baptiste Lafontaine
 * @author Arsène Huot
 * 
 */
public class VectorClock implements Serializable, Cloneable {

	private static final long serialVersionUID = -1437108187463817328L;
	private Map<Integer, Integer> vectorClock;

	public VectorClock() {
		vectorClock = new Hashtable<Integer, Integer>();
	}

	public Integer getEntry(final Integer key) {
		Integer entry;
		entry = vectorClock.get(key);
		if (entry == null) {
			entry = Integer.valueOf(0);
			vectorClock.put(key, entry);
			System.out.println("Vector clock: getEntry, unknown entry '" + key
					+ "', added");
		}
		return entry;
	}

	public void setEntry(final Integer key, final Integer value) {
		Integer entry = vectorClock.get(key);
		if (entry == null) {
			vectorClock.put(key, value);
			System.out.println("Vector clock: setEntry, unknown entry '" + key
					+ "', added with value " + value);
		} else {
			vectorClock.put(key, value);
		}
	}

	public void incementEntry(final Integer key) {
		Integer entry = vectorClock.get(key);
		if (entry == null) {
			vectorClock.put(key, Integer.valueOf(1));
			System.out.println("Vector clock: incementEntry, unknown entry '"
					+ key + "', added and value = 0");
		} else {
			int oldValue = entry;
			vectorClock.remove(key);
			vectorClock.put(key, Integer.valueOf(oldValue + 1));
		}
	}

	public Object clone() {
		VectorClock clone = null;
		try {
			clone = (VectorClock) super.clone();
			clone.vectorClock = new Hashtable<Integer, Integer>();
		} catch (CloneNotSupportedException e) {
			System.out.println(e);
		}
		for (Integer key : vectorClock.keySet()) {
			Integer entry = vectorClock.get(key);
			clone.vectorClock.put(key, entry);
		}
		return clone;
	}

	public void max(final VectorClock other) {
		// TODO : fixme
		if (other != null) {
			for (Integer key : other.vectorClock.keySet()) {
				if (other.getEntry(key) > this.getEntry(key)) {
					/* Mise à jour de la valeur */
					this.setEntry(key, other.getEntry(key));
				}
			}
		}
	}

	public boolean isPrecededByAndFIFO(final VectorClock msgVC,
			final int sender) {
		boolean preceds = true;

		for (Integer key : msgVC.vectorClock.keySet()) {
			//System.out.println("key:" + key + "v[q]=" + msgVC.getEntry(key) + ";vp[q]=" + this.getEntry(key));
			if (key != sender) {
				/* q = sender */
				/* pour tout q!=k, v[k] <= vp[k] */
				if (msgVC.getEntry(key) <= this.getEntry(key)) {
					preceds = true;
				}
				else { return false; }
			}
			else {
				/* v[q] = vp[q] + 1 */
				//System.out.println("sender:" + sender + ";key=" + key + ";v[q]=" + msgVC.getEntry(key) + ";vp[q]=" + this.getEntry(key));
				if (msgVC.getEntry(key) == (this.getEntry(key) + 1)) {
					preceds = true;
				}
				else { return false;  }
			}
		}

		return preceds;
	}

	public String toString() {
		return vectorClock.toString();
	}
}
